Compressive Sensing

The task is to extract images or signals accurately and even exactly from a number of samples which is far smaller than the desired resolution of the image/signal, e.g., the number of pixels in the image. This new technique draws from results in several fields

Suppose we are given a sparse signal.

Can we recover the signal with small number of measurements (far smaller than the desired resolution of the signal)?

The answer is _YES, for some signals and carefully selected measurements using $l_1$ minimization._

Prerequisites

The reader should be familiar to elementary concepts about signals, with linear algebra concepts, and linear programming.

Competences

The reader should be able to recover a signal from a small number of measurements.

References

For more details see

Credits: Daniel Bragg, an IASTE Intern, performed testing of some of the methods.

Underdetermined systems

Let $A\in\mathbb{R}^{m\times n}$ with $m<n$, $x\in\mathbb{R}^n$ and $b\in\mathbb{R}^m$.

Definitions

The system $Ax=b$ is underdetermined.

$\|x\|_0$ is the number of nonzero entries of $x$ (a quasi-norm).

A matrix $A$ satisfies the restricted isometry property (RIP) of order $k$ with constant $\delta_k\in(0,1)$ if

$$ (1 − \delta_k )\|x\|_2^2 \leq \| Ax\|_2^2 \leq (1 + \delta_k)\| x\|_2^2 $$

for any $x$ such that $\|x\|_0 \leq k$.

A mutual incoherence of a matrix $A$ is

$$ \mathcal{M}(A)= \max_{i \neq j} |[A^TA]_{ij}|, $$

that is, the absolutely maximal inner product of distinct columns of $A$. If the columns of $A$ have unit norms, $\mathcal{M}(A)\in[0,1]$.

The spark of a given matrix $A$, $spark(A)$, is the smallest number of columns of $A$ that are linearly dependant.

Facts

  1. An underdetermined system either has no solution or has infinitely many solutions.

  2. The typical problem is to choose the solution of some minimal norm. This problem can be reformulated as a constrained optimization problem $$ \textrm{minimize}\ \|x\| \quad \textrm{subject to} \quad Ax=b. $$ In particular:

    1. For the 2-norm, the $l_2$ minimization problem is solved by SVD: let $\mathop{\mathrm{rank}}(A)=r$ and let $A=U\Sigma V^T$ be the SVD of $A$. Then $$ x=\sum_{k=1}^r \frac{U[:,k]^Tb}{\sigma_k} V[:,k]. $$
    2. For the 1-norm, the $l_1$ minimization problem is a linear programming problem $$\textrm{minimize}\ \ c^T x \quad \textrm{subject to} \quad Ax=b,\ x\geq 0,$$ for $c^T=\begin{bmatrix}1 & 1 & \cdots & 1 \end{bmatrix}$.
    3. For the 0-norm, the $l_0$ problem (which appears in compressive sensing) $$ \textrm{minimize}\ \|x\|_0 \quad \textrm{subject to} \quad Ax=b, $$ is NP-hard.
  3. It holds $spark(A)\in[2,m+1]$.
  4. For any vector $b$, there exists at most one vector $x$ such that $\|x\|_0\leq k$ and $Ax=b$ if and only if $spark(A) > 2k$. This implies that $m\geq 2k$, which is a good choice when we are computing solutions which are exactly sparse.

  5. If $k<\displaystyle \frac{1}{2} \left(1+\frac{1}{\mathcal{M}(A)}\right)$, then for any vector $b$ there exists at most one vector $x$ such that $\|x\|_0\leq k$ and $Ax=b$.

  6. If the solution $x$ of $l_0$ problem satisfies $\|x\|_0 < \displaystyle \frac{\sqrt{2}-1/2}{\mathcal{M}(A)}$, then the solution of $l_1$ problem is the solution of $l_0$ problem!

  7. If $A$ has columns of unit-norm, then $A$ satisfies the RIP of order $k$ with $\delta_k = (k − 1)\mathcal{M}(A)$ for all $k < 1/\mathcal{M}(A)$.

  8. If $A$ satisfies RIP of order $2k$ with $\delta_{2k}<\sqrt{2}-1$, then the solution of $l_1$ problem is the solution of $l_0$ problem!

  9. Checking whether the specific matrix has RIP is difficult. If $m ≥ C \cdot k \log\left(\displaystyle\frac{n}{k}\right)$, where $C$ is some constant depending on each instance, the following classes of matrices satisfy RIP with $\delta_{2k}<\sqrt{2}-1$ with overwhelming probability(the matrices are normalised to have columns with unit norms):

    1. Form $A$ by sampling at random $n$ column vectors on the unit sphere in $\mathbb{R}^m$.
    2. Form $A$ by sampling entries from the normal distribution with mean 0 and variance $1/ m$.
    3. Form $A$ by sampling entries from a symmetric Bernoulli distribution $P(A_{ij} = ±1/\sqrt{m}) = 1/2$.
    4. Form $A$ by sampling at random $m$ rows of the Fourier matrix.
  10. The compressive sensing interpretation is the following: the signal $x$ is reconstructed from samples with $m$ functionals (the rows of $A$).

Example - $l_2$ minimization


In [1]:
using Random
Random.seed!(678)
m=5
n=8
A=rand(m,n)
b=rand(m)
A


Out[1]:
5×8 Array{Float64,2}:
 0.655726   0.281598  0.031207   0.952019   …  0.0400642  0.401826  0.509873
 0.237319   0.513194  0.913369   0.292343      0.198013   0.982805  0.881078
 0.498884   0.394471  0.677593   0.244604      0.751218   0.687313  0.108431
 0.0113688  0.468253  0.157199   0.384663      0.825939   0.537428  0.961668
 0.0362182  0.150309  0.0367145  0.0380349     0.146018   0.13143   0.608996

In [2]:
b


Out[2]:
5-element Array{Float64,1}:
 0.4179957693889931
 0.43562304885058234
 0.5852978448978767
 0.09913808966202886
 0.832664764801347

In [3]:
using LinearAlgebra
x=A\b
U,σ,V=svd(A)
norm(A*x-b), norm( sum( [(U[:,k]'*b/σ[k])[1]*V[:,k]  for k=1:m])-x)


Out[3]:
(1.383330760426861e-15, 1.5779272065485058e-15)

Examples - Exact sparse signal recovery

We recover randomly generated sparse signals "measured" with rows of the matrix $A$. The experiment is performed for types of matrices from Fact 9.

The $l_1$ minimization problem is solved using the function linprog() from the package MathProgBase.jl. This function requires the linear programming solver from the package Clp.jl be installed beforehand (it is a longer compilation). MathProgBase.jl is now depraected in favour of MathOptInterface.jl, but we keep it for this lecture for the sake of simplicity. Good choice is also JuMP.jl.

Random matrices are generated using the package Distributions.jl.

For more details see the documentation.


In [4]:
# import Pkg; Pkg.add("Clp")
# import Pkg; Pkg.add("GLPKMathProgInterface")
# import Pkg; Pkg.add("MathProgBase")
# import Pkg; Pkg.add("Distributions")

In [5]:
using Plots
using Clp
using GLPKMathProgInterface
# using Gurobi
using MathProgBase
using Distributions

In [6]:
varinfo(MathProgBase)


Out[6]:
name size summary
MathProgBase 541.819 KiB Module
buildlp 0 bytes typeof(buildlp)
linprog 0 bytes typeof(linprog)
mixintprog 0 bytes typeof(mixintprog)
quadprog 0 bytes typeof(quadprog)
solvelp 0 bytes typeof(solvelp)

Small example

\begin{split}\min_{x,y}\, &-x\\ s.t.\quad &2x + y \leq 1.5\\ & x \geq 0, y \geq 0\end{split}

In [7]:
l1 = linprog([-1,0],[2 1],'<',1.5,ClpSolver())


Out[7]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, -0.75, [0.75, 0.0], Dict{Any,Any}(:lambda => [-0.5],:redcost => [0.0, 0.5]))

In [8]:
fieldnames(typeof(l1))


Out[8]:
(:status, :objval, :sol, :attrs)

In [9]:
l1.attrs


Out[9]:
Dict{Any,Any} with 2 entries:
  :lambda  => [-0.5]
  :redcost => [0.0, 0.5]

In [10]:
# Random vectors on a unit sphere
using Random
Random.seed!(421)
n=100
m=40
k=15
A=svd(rand(m,n)).Vt
using SparseArrays
# Compute a random vector
x=sprand(n,k/n)


Out[10]:
100-element SparseVector{Float64,Int64} with 11 stored entries:
  [16 ]  =  0.181374
  [19 ]  =  0.135144
  [20 ]  =  0.167985
  [34 ]  =  0.0388711
  [37 ]  =  0.650376
  [39 ]  =  0.235729
  [47 ]  =  0.401863
  [55 ]  =  0.115655
  [63 ]  =  0.548485
  [74 ]  =  0.213844
  [86 ]  =  0.859015

In [11]:
A


Out[11]:
40×100 Array{Float64,2}:
 -0.112014     -0.0939869   -0.0961287  …  -0.0958968   -0.100484
 -0.00449496   -0.111701     0.0884182      0.00457911  -0.149988
  0.077177     -0.0646014   -0.0269624      0.0315794   -0.195342
  0.027563     -0.01658     -0.0737059      0.0644098    0.0505977
  0.0596371     0.278137     0.0580865     -0.110824     0.0625572
  0.0100593     0.0182076    0.0221766  …   0.050186    -0.0311303
  0.125236      0.135701     0.0212444     -0.132585    -0.0656351
 -0.0281914     0.0070435    0.075629       0.0615999    0.0346904
  0.229633      0.0990928   -0.116769      -0.0675747    0.226441
  0.011882      0.023134    -0.172375       0.0616146    0.0112694
  0.0519892     0.174056    -0.09774    …   0.30615      0.0358746
  0.0480932    -0.0651821   -0.124658       0.0452374    0.15932
 -0.0123457     0.134625    -0.111294      -0.038966    -0.0749589
  ⋮                                     ⋱               
 -0.0710764    -0.197602     0.0165554     -0.0474051   -0.0539919
 -0.0181888     0.145202     0.0322125      0.00459761  -0.0139227
  0.109287      0.0511806    0.0737905  …  -0.00599124  -0.0585657
  0.0255026     0.0224512    0.0106179     -0.0822379   -0.121766
  0.000499589   0.00951941  -0.156227      -6.14603e-5   0.0103461
  0.0320927    -0.00773827   0.20154        0.127442     0.0896254
  0.0985013     0.0714065    0.0246659     -0.0262897    0.0503406
  0.127068      0.0222845   -0.0933533  …   0.115024    -0.041989
  0.0842555     0.00113959  -0.0790689      0.0235465   -0.0477832
 -0.149573      0.00944482  -0.187974      -0.0485976    0.0179155
 -0.139025     -0.152064     0.0340547      0.165909    -0.0337065
 -0.180989     -0.135428     0.102085      -0.0347484    0.138303

In [12]:
for i=1:size(A,2)
    normalize!(view(A,:,i))
end

In [13]:
diag(A'*A)


Out[13]:
100-element Array{Float64,1}:
 1.0000000000000002
 1.0000000000000002
 0.9999999999999999
 0.9999999999999998
 0.9999999999999997
 0.9999999999999999
 1.0000000000000002
 1.0000000000000002
 0.9999999999999997
 1.0
 0.9999999999999997
 0.9999999999999996
 1.0000000000000004
 ⋮
 1.0000000000000004
 0.9999999999999994
 0.9999999999999999
 1.0000000000000002
 1.0000000000000004
 1.0000000000000002
 0.9999999999999998
 1.0
 1.0000000000000002
 1.0
 0.9999999999999997
 1.0000000000000004

In [14]:
# Check incoherence
μ=maximum(abs,A'*A-I)


Out[14]:
0.47072618408416944

In [15]:
# Sampling
b=A*x


Out[15]:
40-element Array{Float64,1}:
 -0.5547149521112036
  0.25972046341264815
  0.11370341373427321
  0.151833328875101
 -0.02326073706693454
  0.0015301867505390504
  0.21898700675776828
  0.214690295440623
  0.005683497040788188
  0.17804307831510738
 -0.049539117414256664
  0.12436150395181446
  0.1606746539446208
  ⋮
  0.2476481926736151
 -0.08669347750182461
 -0.41037250728439356
  0.07661764637014175
  0.547624557463909
  0.0033807446712582276
 -0.019994932720417517
  0.09482375772820863
 -0.12363981152282301
 -0.14903415897443145
  0.3975199069055977
  0.06390179999790022

In [16]:
# Recovery
c=ones(n)
l1=linprog(c,A,'=',b,0,Inf,ClpSolver())


Out[16]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, 3.548341193490908, [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Dict{Any,Any}(:lambda => [0.0, 0.0, 0.0, 0.0, 0.0, -0.5621361837327901, 0.0, 2.7649786351522625, -1.1506859697490217, -1.0060739977229174  …  -1.4041479126572265, 0.726872878934839, 0.9243198638395849, -0.7004415902169839, 0.0, -0.6559797692673628, -0.5791775741894218, 0.0, 0.0, -0.5918572374245012],:redcost => [3.0806151845743464, 0.9786638560305045, 0.5106221314577065, 1.2102124529494365, 0.0, 0.3972141769230823, 3.2460930291799666, 0.23541338567220363, 1.600900795869234, 0.0  …  0.0, 0.13824972677062625, 1.3584333116080518, 1.3449041979979233, 1.878272024034487, 0.5991569829132826, 2.370253446107059, 0.0, 0.7341041773231957, 0.280053236435309]))

In [17]:
# Check
scatter(x)


Out[17]:

In [18]:
x


Out[18]:
100-element SparseVector{Float64,Int64} with 11 stored entries:
  [16 ]  =  0.181374
  [19 ]  =  0.135144
  [20 ]  =  0.167985
  [34 ]  =  0.0388711
  [37 ]  =  0.650376
  [39 ]  =  0.235729
  [47 ]  =  0.401863
  [55 ]  =  0.115655
  [63 ]  =  0.548485
  [74 ]  =  0.213844
  [86 ]  =  0.859015

In [19]:
scatter!(l1.sol)


Out[19]:

In [20]:
sparse(l1.sol)


Out[20]:
100-element SparseVector{Float64,Int64} with 11 stored entries:
  [16 ]  =  0.181374
  [19 ]  =  0.135144
  [20 ]  =  0.167985
  [34 ]  =  0.0388711
  [37 ]  =  0.650376
  [39 ]  =  0.235729
  [47 ]  =  0.401863
  [55 ]  =  0.115655
  [63 ]  =  0.548485
  [74 ]  =  0.213844
  [86 ]  =  0.859015

In [22]:
# for n=50:50:500, m=20:10:200, k=10:10:100
# m should not be too small
n=200
m=40
k=15
A=svd(rand(m,n)).Vt
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
scatter([x l1.sol])


Out[22]:

In [23]:
# Normal distribution
# for n=50:50:500, m=20:10:200, k=10:10:100
n=200
m=50
k=10
A=rand(Normal(0,1/m),m,n)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
# l1=linprog(ones(n),A,'=',b,0.0,Inf,GurobiSolver())
scatter([x l1.sol])


Out[23]:

In [24]:
A


Out[24]:
50×200 Array{Float64,2}:
 -0.128142    -0.0618582   0.289588    …   0.191438    0.0637886
 -0.0498817    0.0858138  -0.0314703      -0.0436682  -0.112609
  0.239702     0.30347    -0.083479       -0.154262    0.0355659
  0.215406    -0.0635525  -0.29067         0.0037414   0.0475742
 -0.00620356   0.0237975   0.359133       -0.0837862  -0.125024
  0.133542     0.161625    0.121367    …  -0.112516    0.00913314
  0.239598    -0.289608   -0.00938237     -0.202717    0.148889
  0.144759     0.110974    0.129584       -0.17592    -0.131045
  0.072055     0.0681145  -0.0461465      -0.0023313   0.0876257
  0.0514757    0.129224   -0.0518804      -0.264487    0.102813
  0.0285191    0.105777    0.189561    …  -0.354245   -0.206685
  0.0338725   -0.333488    0.0458806       0.0820801   0.1746
 -0.0259985    0.061846   -0.180843       -0.129767   -0.139001
  ⋮                                    ⋱              
 -0.115652     0.0716736  -0.0431867       0.104692   -0.0031967
  0.37061      0.164595   -0.137208        0.113907    0.144097
 -0.145768     0.0749632  -0.0328243   …  -0.15763    -0.0800995
 -0.173139     0.0494743   0.132863        0.13903    -0.256062
  0.0300249   -0.14905    -0.161307       -0.0594939  -0.214669
  0.123565     0.0236346  -0.0568771      -0.0167559  -0.280926
  0.00273922  -0.108104    0.0201316       0.0110046  -0.0340808
 -0.0536193   -0.365716    0.0438703   …   0.334829   -0.0763543
 -0.0633343    0.0520604  -0.0865358      -0.0431743  -0.158902
  0.0329237    0.199633    0.225138       -0.339373   -0.211575
 -0.0317611   -0.0646857   0.131435        0.141946   -0.115966
 -0.0740069   -0.201696   -0.180946       -0.0672697  -0.116984

In [25]:
# Symmetric Bernoulli distribution
# for n=50:50:500, m=20:10:200, k=10:10:100
n=200
m=50
k=15
# The matrix of (-1,1)s
A=2*(rand(Bernoulli(0.5),m,n).-0.5)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
scatter([x l1.sol])


Out[25]:

In [26]:
nnz(x)


Out[26]:
17

In [27]:
A


Out[27]:
50×200 Array{Float64,2}:
 -0.141421  -0.141421  -0.141421  …  -0.141421  -0.141421   0.141421
 -0.141421   0.141421   0.141421     -0.141421  -0.141421   0.141421
 -0.141421   0.141421   0.141421      0.141421  -0.141421   0.141421
  0.141421   0.141421  -0.141421     -0.141421  -0.141421  -0.141421
 -0.141421   0.141421  -0.141421     -0.141421   0.141421  -0.141421
  0.141421   0.141421  -0.141421  …  -0.141421  -0.141421   0.141421
 -0.141421  -0.141421   0.141421     -0.141421  -0.141421   0.141421
 -0.141421   0.141421  -0.141421      0.141421   0.141421   0.141421
  0.141421   0.141421   0.141421     -0.141421   0.141421   0.141421
  0.141421  -0.141421  -0.141421      0.141421   0.141421   0.141421
  0.141421  -0.141421  -0.141421  …   0.141421   0.141421  -0.141421
 -0.141421  -0.141421   0.141421      0.141421  -0.141421   0.141421
 -0.141421   0.141421   0.141421     -0.141421  -0.141421   0.141421
  ⋮                               ⋱                        
  0.141421  -0.141421   0.141421     -0.141421  -0.141421   0.141421
  0.141421  -0.141421  -0.141421     -0.141421  -0.141421   0.141421
 -0.141421   0.141421  -0.141421  …   0.141421  -0.141421   0.141421
  0.141421   0.141421   0.141421     -0.141421  -0.141421   0.141421
 -0.141421   0.141421   0.141421     -0.141421   0.141421   0.141421
  0.141421  -0.141421  -0.141421      0.141421   0.141421  -0.141421
 -0.141421   0.141421  -0.141421     -0.141421   0.141421  -0.141421
 -0.141421  -0.141421  -0.141421  …  -0.141421  -0.141421  -0.141421
 -0.141421  -0.141421   0.141421      0.141421   0.141421  -0.141421
  0.141421  -0.141421  -0.141421     -0.141421  -0.141421  -0.141421
 -0.141421   0.141421   0.141421      0.141421   0.141421   0.141421
 -0.141421   0.141421  -0.141421     -0.141421  -0.141421   0.141421

For Fourier transformation and Fourier matrix we use package FFTW.jl.


In [28]:
using FFTW

In [29]:
varinfo(FFTW)


Out[29]:
name size summary
AbstractFFTs 323.169 KiB Module
FFTW 337.112 KiB Module
Frequencies 40 bytes UnionAll
bfft 0 bytes typeof(bfft)
bfft! 0 bytes typeof(bfft!)
brfft 0 bytes typeof(brfft)
dct 0 bytes typeof(dct)
dct! 0 bytes typeof(dct!)
fft 0 bytes typeof(fft)
fft! 0 bytes typeof(fft!)
fftfreq 0 bytes typeof(fftfreq)
fftshift 0 bytes typeof(fftshift)
idct 0 bytes typeof(idct)
idct! 0 bytes typeof(idct!)
ifft 0 bytes typeof(ifft)
ifft! 0 bytes typeof(ifft!)
ifftshift 0 bytes typeof(ifftshift)
irfft 0 bytes typeof(irfft)
plan_bfft 0 bytes typeof(plan_bfft)
plan_bfft! 0 bytes typeof(plan_bfft!)
plan_brfft 0 bytes typeof(plan_brfft)
plan_dct 0 bytes typeof(plan_dct)
plan_dct! 0 bytes typeof(plan_dct!)
plan_fft 0 bytes typeof(plan_fft)
plan_fft! 0 bytes typeof(plan_fft!)
plan_idct 0 bytes typeof(plan_idct)
plan_idct! 0 bytes typeof(plan_idct!)
plan_ifft 0 bytes typeof(plan_ifft)
plan_ifft! 0 bytes typeof(plan_ifft!)
plan_irfft 0 bytes typeof(plan_irfft)
plan_rfft 0 bytes typeof(plan_rfft)
rfft 0 bytes typeof(rfft)
rfftfreq 0 bytes typeof(rfftfreq)

In [30]:
# Fourier matrix
# for n=50:50:500, m=20:10:200, k=10:10:100
n=200
m=50
k=15
# Elegant way of computing the Fourier matrix
F=fft(Matrix(I,n,n),1)
# Select m/2 random rows
ind=Random.randperm(n)[1:round(Int,m/2)]
Fm=F[ind,:]
# We need to work with real matrices due to linprog()
A=[real(Fm); imag(Fm)]
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
scatter([x l1.sol])


Out[30]:

Signal recovery from noisy observations

In the presence of noise in observation, we want to recover a vector $x$ from $b=Ax + z$, where $z$ is a stochastic or deterministic unknown error term.

Definition

The hard thresholding operator, $H_k(x)$, sets all but the $k$ entries of $x$ with largest magnitude to zero.

Facts

  1. The problem can be formulated as $l_1$ minimization problem $$ \textrm{minimize}\ \|x\|_1 \quad \textrm{subject to} \quad \|Ax-b\|_2^2\leq\epsilon, $$ where $\epsilon$ bounds the amount of noise in the data.

  2. Assume that $A$ satisfies RIP of order $2k$ with $\delta_{2k}< \sqrt{2}-1$. Then the solution $x^{\star}$ of the above problem satisfies $$ \|x^{\star}-x\|_2 \leq C_0 \displaystyle \frac{1}{\sqrt{k}}\|x-H_k(x)\|_1 +C_1\epsilon, $$ where $x$ is the original signal.

  3. The $l_1$ problem is a convex programming problem and can be efficiently solved. The solution methods are beyond the scope of this course.

  4. If $k$ is known in advance, $A$ satisfies RIP with $\delta_{3k}<1/15$, and $\|A\|_2<1$, the $k$-sparse aproximation of $x$ can be computed by the Iterative Hard Thresholding algorithm

    1. Initialization: $x=0$.
    2. Iteration: repeat until convergence $x=H_k(x+A^T(b-Ax))$.

Example

We construct the $k$ sparse $x$, form $b$, add noise, and recover it with the algorithm from Fact 4. The conditions on $A$ are rather restrictive, which means that $k$ must be rather small compared to $n$ and $m$ must be rather large. For convergence, we limit the number of iterations to $50m$.


In [42]:
n=300
# k is small compared to n
k=8
x=10*sprand(n,k/n)
# Reset k
k=nnz(x)
# Define m, rather large
m=5*round(Int,k*log(n/k))
# m=100
# Sampling matrix - normal distribution
A=rand(Normal(0,1/m),m,n)
# A=A/(norm(A)+1)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Form b
b=A*x
# Add noise
noise=rand(m)*1e-5
b.+=noise


Out[42]:
195-element Array{Float64,1}:
 -0.10080747440530438
 -0.7209380507808525
  1.0473483467057594
  0.24956039589804677
  0.9006967598788206
  0.046910816963848134
 -2.5600519247229747
 -0.2083743753895718
  0.8686312403792671
 -0.5608575411423606
 -3.1418432605240016
 -0.4611701964644132
 -0.5310919473765977
  ⋮
  0.7518669059569555
  0.20332176598493806
 -0.47691913906864514
  3.3480582201194276
 -1.189407390612627
 -0.2486272501289412
 -0.9798880745422227
  1.6320040808756617
 -2.383329159661344
  0.872447945551356
 -1.4118989667753572
 -0.6020495570477037

In [43]:
m


Out[43]:
195

In [44]:
A


Out[44]:
195×300 Array{Float64,2}:
  0.0827642   -0.101608    -0.0439999   …   0.0116662   -0.0320591
  0.109782    -0.0127721   -0.117962        0.0730588   -0.0744445
 -0.0747757   -0.0456583    0.0237722      -0.0593973   -0.0539921
 -0.034814    -0.0680417   -0.0508666      -0.0422623    0.0117048
  0.0350621    0.0260893    0.025651       -0.0360158    0.111327
 -0.0620307   -0.0574071   -0.0322572   …  -0.0506751    0.018435
 -0.0731914   -0.0247299   -0.0239332      -0.0333245    0.0300679
  0.0805554    0.00282736  -0.0486261      -0.0312568   -0.0442209
 -0.0670386   -0.0387721   -0.0889916      -0.0768119   -0.080019
  0.0807885    0.00584034  -0.00778799     -0.0276608    0.00644086
 -0.14124      0.0292271   -0.108013    …   0.148809    -0.0600613
 -0.0316593    0.0545868   -0.056894       -0.050113    -0.0792173
  0.0125339   -0.0160611    0.166593        0.0169021    0.0434198
  ⋮                                     ⋱               
 -0.0923739    0.0589752   -0.10676        -0.0452954    0.107508
 -0.0258323    0.0688695    0.0539229       0.0377123    0.0748832
 -0.0042573   -0.0639797   -0.0819194   …  -0.0519728   -0.0966332
 -0.069298    -0.0404686   -0.143001        0.0614736   -0.0312675
  0.0792143    0.0714542   -0.0506785      -0.0190003    0.0783642
  0.00693609  -0.0242637   -0.0912834      -0.118333     0.0357065
  0.0103403    0.0566303   -0.00712353     -0.0178761   -0.0615983
  0.017705    -0.138606     0.0556958   …   0.0726234    0.0708154
  0.0387682   -0.0414501   -0.00011591     -0.0173407    0.0062154
 -0.150263    -0.032704    -0.0186858      -0.0852192   -0.0108948
  0.027685     0.0925466    0.0801259      -0.00924069   0.055839
  0.0648476    0.113547    -0.0145849       0.064234    -0.0736971

In [45]:
norm(A),k,m


Out[45]:
(17.320508075688775, 12, 195)

In [46]:
x


Out[46]:
300-element SparseVector{Float64,Int64} with 12 stored entries:
  [29 ]  =  3.82859
  [42 ]  =  9.63896
  [77 ]  =  0.591236
  [89 ]  =  5.22299
  [167]  =  8.67328
  [172]  =  7.29686
  [185]  =  0.467361
  [195]  =  0.999532
  [208]  =  1.47997
  [256]  =  1.43769
  [257]  =  7.5885
  [296]  =  3.67046

In [47]:
# Iterative Hard Thresholding 
function H(x::Vector,k::Int)
    y=deepcopy(x)
    ind=sortperm(abs.(y),rev=true)
    y[ind[k+1:end]].=0
    y
end


Out[47]:
H (generic function with 1 method)

In [48]:
function IHT(A::Matrix, b::Vector,k::Int)
    # Tolerance
    τ=1e-12
    x=zeros(size(A,2))
    for i=1:50*m
        x=H(x+A'*(b-A*x),k)
    end
    x
end


Out[48]:
IHT (generic function with 1 method)

In [49]:
y=IHT(A,b,k)
norm(A*x-b)/norm(b)


Out[49]:
4.780396883468375e-6

In [50]:
println(x)


  [29 ]  =  3.82859
  [42 ]  =  9.63896
  [77 ]  =  0.591236
  [89 ]  =  5.22299
  [167]  =  8.67328
  [172]  =  7.29686
  [185]  =  0.467361
  [195]  =  0.999532
  [208]  =  1.47997
  [256]  =  1.43769
  [257]  =  7.5885
  [296]  =  3.67046

In [51]:
println(sparse(y))


  [29 ]  =  3.82859
  [42 ]  =  9.63897
  [77 ]  =  0.591242
  [89 ]  =  5.223
  [167]  =  8.67328
  [172]  =  7.29686
  [185]  =  0.467361
  [195]  =  0.999546
  [208]  =  1.47998
  [256]  =  1.4377
  [257]  =  7.5885
  [296]  =  3.67046

In [52]:
scatter([x y])


Out[52]:

Let us try linear programing in the case of noisy observations.


In [53]:
# Try with noise
# Normal distribution
# for n=50:50:500, m=20:10:200, k=10:10:100
n=300
# k is small compared to n
k=8
m=5*round(Int,k*log(n/k))
A=rand(Normal(0,1/m),m,n)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=1*sprand(n,k/n)
# Sampling with noise
b=A*x
# Add noise
noise=(rand(m).-0.5)*1e-3
b.+=noise
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
# method=:Simplex or method=:InteriorPoint
#l1=linprog(ones(n),A,'=',b,0.0,Inf,
#     GLPKSolverLP(presolve=true,method=:Simplex))


Out[53]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, 5.0033716695189705, [0.0008303466795488528, 0.0, 0.0006543191209309684, 0.0, 0.0004190322248019799, 0.0007391268642454055, 0.0003415136688064221, 0.0005790024492297402, 0.0, 0.0005047813118937312  …  0.0, 0.0, 0.000500144538871533, 0.0, 0.0, 0.0, 0.0, 0.0005732273915702031, 0.0, 0.0], Dict{Any,Any}(:lambda => [-8.769220027302339, -4.861305539750599, 7.0921199214055415, -9.055477756804574, -4.502977765971061, 3.436711607121686, 2.463445948725318, -3.532014227158262, 6.175816863974137, -5.134647126740207  …  1.3278402772956428, -3.116762789063801, -4.410676404593804, 5.818764120497179, 3.9382035519681353, -7.654412117896313, -2.548611520779456, -1.6471736183482375, -1.5455043806214677, 1.3871364288093206],:redcost => [0.0, 3.318569882190189, 0.0, 2.397677468149725, 0.0, 0.0, 0.0, 0.0, 4.887344317118203, 0.0  …  2.293168028321813, 2.1753251951654904, 0.0, 7.560450296773302, 11.755817220782314, 2.395146970159645, 3.250318951912172, 0.0, 0.9631351690797699, 4.836558682911122]))

In [54]:
scatter([x l1.sol])


Out[54]:

In [55]:
x


Out[55]:
300-element SparseVector{Float64,Int64} with 10 stored entries:
  [83 ]  =  0.652797
  [91 ]  =  0.823785
  [134]  =  0.400249
  [188]  =  0.0588333
  [215]  =  0.101926
  [229]  =  0.29591
  [248]  =  0.889825
  [263]  =  0.651337
  [272]  =  0.28864
  [275]  =  0.77161

In [56]:
sparse(l1.sol)


Out[56]:
300-element SparseVector{Float64,Int64} with 145 stored entries:
  [1  ]  =  0.000830347
  [3  ]  =  0.000654319
  [5  ]  =  0.000419032
  [6  ]  =  0.000739127
  [7  ]  =  0.000341514
  [8  ]  =  0.000579002
  [10 ]  =  0.000504781
  [12 ]  =  0.000184353
  [14 ]  =  1.92967e-5
  [15 ]  =  0.000217225
         ⋮
  [276]  =  0.000860071
  [278]  =  0.00114453
  [279]  =  0.000382649
  [280]  =  7.59433e-5
  [281]  =  0.000250437
  [284]  =  0.000241325
  [285]  =  0.000218012
  [286]  =  0.000391028
  [288]  =  0.000153847
  [293]  =  0.000500145
  [298]  =  0.000573227

In [57]:
# But 
l1.sol[31]


Out[57]:
0.0

Sensing images

Wavelet transformation of an image is essentially sparse, since only small number of cofficients is significant. This fact can be used for compression.

Wavelet transforms are implemented the package Wavelets.jl.

Example - Lena

The tif version of the image has 65_798 bytes, the png version has 58_837 bytes, and the jpeg version has 26_214 bytes.


In [58]:
# import Pkg; Pkg.add("Wavelets")
# import Pkg; Pkg.add("TestImages")

In [59]:
using Wavelets
using Images
using TestImages

In [60]:
varinfo(TestImages)


Out[60]:
name size summary
TestImages 15.274 KiB Module
testimage 0 bytes typeof(testimage)

In [61]:
println(TestImages.remotefiles)


["autumn_leaves.png", "blobs.gif", "cameraman.tif", "earth_apollo17.jpg", "fabio_color_256.png", "fabio_color_512.png", "fabio_gray_256.png", "fabio_gray_512.png", "hela-cells.tif", "house.tif", "jetplane.tif", "lake_color.tif", "lake_gray.tif", "lena_color_256.tif", "lena_color_512.tif", "lena_gray_256.tif", "lena_gray_512.tif", "lena_gray_16bit.png", "livingroom.tif", "lighthouse.png", "mandril_color.tif", "mandril_gray.tif", "mandrill.tiff", "m51.tif", "moonsurface.tiff", "mountainstream.png", "mri-stack.tif", "multi-channel-time-series.ome.tif", "peppers_color.tif", "peppers_gray.tif", "pirate.tif", "toucan.png", "walkbridge.tif", "woman_blonde.tif", "woman_darkhair.tif"]

In [62]:
img=testimage("lena_gray_256.tif")


Out[62]:

In [63]:
typeof(img)


Out[63]:
Array{Gray{Normed{UInt8,8}},2}

In [64]:
size(img)


Out[64]:
(256, 256)

In [65]:
show(img[1,1])


Gray{N0f8}(0.635)

In [66]:
# Convert the image to 32 or 64 bit floats
x=map(Float32,img)
" Number of matrix elements = ",prod(size(x)), 
" Bytes = ",sizeof(x), size(x)


Out[66]:
(" Number of matrix elements = ", 65536, " Bytes = ", 262144, (256, 256))

In [67]:
?modwt


search: modwt imodwt maxmodwttransformlevels methodswith

Out[67]:

Perform a maximal overlap discrete wavelet transform (MODWT) of the array x. The wavelet type wt determines the transform type and the wavelet class, see wavelet. (Note that the wavelet filter coefficients are scaled by 1/√2 for the MODWT so that the transform maintains unit energy).

The number of transform levels L can be any number <= maxmodwttransformlevels(x) (the default value).

Returns an n × L+1 matrix (where n is the length of x) with the wavelet coefficients for level j in column j. The scaling coefficients are in the last (L+1th) column.


In [83]:
# Compute the wavelet transform of x
# or wavelet(WT.db3)
wlet=wavelet(WT.Coiflet{4}(), WT.Filter, WT.Periodic)
# wlet=wavelet(WT.haar)
xₜ=dwt(x,wlet,2);

In [84]:
colorview(Gray,real(xₜ))


Out[84]:

We now set all but the 10% absolutely largest coefficients to zero and reconstruct the image. The images are very similar, which illustrates that the wavelet transform of an image is essentially sparse.


In [85]:
ind=sortperm(abs.(vec(xₜ)),rev=true)
# 0.1 = 10%, try also 0.05 = 5%
τ=0.05
k=round(Int,τ*length(ind))
xsparse=copy(xₜ)
xsparse[ind[k+1:end]].=0;

In [86]:
using SparseArrays
nnz(sparse(xsparse))


Out[86]:
3277

In [87]:
colorview(Gray,xsparse)


Out[87]:

In [89]:
# Inverse wavelet transform of the sparse data
imgsparse=idwt(xsparse,wlet,2)
# Original and sparse image
display(img)
display(colorview(Gray,imgsparse))


There are $k=3277 $ nonzero coefficients in a sparse wavelet representation.

Is there the sensing matrix which can be used to sample and recover xsparse?

Actual algorithms are elaborate. For more details see J. Romberg, Imaging via Compressive Sampling and Duarte et al.,Single-Pixel Imaging via Compressive Sampling.


In [90]:
using SparseArrays
nnz(sparse(xsparse))


Out[90]:
3277

In [91]:
# Columnwise number of non-zeros 
nz=[nnz(sparse(xsparse[:,i])) for i=1:size(x,2)]
display(nz)
maximum(nz)


256-element Array{Int64,1}:
 52
 64
 64
 64
 64
 63
 47
 48
 47
 43
 44
 43
 50
  ⋮
  0
  0
  0
  0
  0
  0
  0
  0
  0
  0
  0
  0
Out[91]:
64

In [92]:
n=size(xsparse,2)
m=150
A=rand(Normal(0,1/m),m,n)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Sampling (columnwise)
b=A*xsparse


Out[92]:
150×256 Array{Float64,2}:
 -1.12166   -1.48052    -1.98934      …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.34321    1.13026     1.38416         0.0  0.0  0.0  0.0  0.0  0.0  0.0
  1.05003    1.78205     2.04036         0.0  0.0  0.0  0.0  0.0  0.0  0.0
  1.3213     0.565708    0.449913        0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.210813  -0.0857042  -0.000493659     0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.389915   1.19726     1.23967      …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.853779   2.08382     2.52196         0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.214554   0.868625    0.913886        0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.479167   0.409168    0.562687        0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.782567   1.04925     1.45985         0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -1.0549    -1.46064    -1.92756      …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.81618   -1.52092    -1.34804         0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.11875   -0.350498   -0.76346         0.0  0.0  0.0  0.0  0.0  0.0  0.0
  ⋮                                   ⋱       ⋮                        ⋮
 -1.33318   -1.58614    -1.7299          0.0  0.0  0.0  0.0  0.0  0.0  0.0
  1.21187    0.312034    0.235028        0.0  0.0  0.0  0.0  0.0  0.0  0.0
  1.00581   -0.459178   -0.396127     …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.987853   1.37757     1.29349         0.0  0.0  0.0  0.0  0.0  0.0  0.0
  1.6618     1.10925     1.79396         0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -2.19683   -2.90423    -3.30611         0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.173656  -0.475985   -0.775246        0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.742418  -0.657978   -1.11261      …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  2.24294    2.9594      3.73219         0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.741882   1.19829     1.85357         0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -2.27483   -2.24127    -2.79836         0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -1.10003   -1.96836    -2.5822          0.0  0.0  0.0  0.0  0.0  0.0  0.0

In [93]:
# Recovery
xrecover=similar(xsparse)
for l=1:size(xsparse,2)
    l1=linprog(ones(n),A,'=',b[:,l],ClpSolver())
    xrecover[:,l]=l1.sol
end

In [94]:
# Mutual incoherence
maximum(abs.(A'*A-I))


Out[94]:
0.33289186321702063

In [95]:
length(b),sizeof(b),size(b)


Out[95]:
(38400, 307200, (150, 256))

In [96]:
sizeof(A)


Out[96]:
307200

In [98]:
imgrecover=idwt(xrecover, wlet, 2)
# imgrecover=idct(xrecover)
# Original and recovered image
display(img)
display(colorview(Gray,imgrecover))


Let us also test the method on a simpler image.


In [99]:
img=load("./files/RDuarte.png")


Out[99]:

In [100]:
size(img)


Out[100]:
(188, 188)

In [101]:
typeof(img)


Out[101]:
Array{RGBA{Normed{UInt8,8}},2}

In [103]:
x=map(Gray,img)
x=map(Float32,x)
xₜ=dwt(x,wlet,2)


Out[103]:
188×188 Array{Float32,2}:
  0.437342      0.464983      0.435268     …   0.000556656   0.000826031
  0.423058      0.429918      0.409581         0.0026452    -0.000135994
  0.452771      0.437121      0.428003        -1.9072f-5     0.00225635
  0.44582       0.441884      0.430949        -0.00406583    0.00122102
  0.435148      0.407638      0.424049        -0.00185222    0.00116998
  0.447214      0.432834      0.459095     …  -0.0024936     0.00149633
  0.466739      0.46553       0.485944        -0.00226423    0.00114636
  0.47291       0.466418      0.476396         0.00143362    0.00210104
  0.450771      0.450981      0.435287        -0.000379255   0.0024555
  0.42809       0.420856      0.406276        -0.00263589   -0.000343247
  0.429873      0.434424      0.443018     …  -0.0019143     0.000475914
  0.469399      0.481726      0.493102        -0.00229982    0.00041217
  0.448869      0.46619       0.484553        -0.000621554  -0.000105109
  ⋮                                        ⋱                
 -0.000337286   0.000107951   0.000361117      3.39446f-5    0.000142815
 -0.00232572   -0.00224633   -0.00244627      -6.70157f-5   -0.000776163
 -0.000480788  -0.000210657  -0.000480037      6.42974f-5    8.63129f-5
 -0.000294557  -0.000269116   0.000442939     -5.27288f-5   -8.29172f-5
 -0.000758367   0.000906135  -0.00190059   …   0.000652851   0.000578762
  0.00101569   -0.000484898  -0.000394573     -0.000262995   0.00186715
  0.0019804    -0.000260425  -0.00119289       0.000422987   0.000467745
  0.000529004   0.0002278     0.00182896      -0.00020752   -0.00060723
 -0.00116426   -0.000139035  -0.000182098      0.0023066    -0.000499345
  0.00112386    0.00114351   -0.000437088  …  -0.00192233    0.000771563
 -0.000336321   0.00023412    0.00270577       0.000518472   0.000407739
  0.000507142  -0.00173613   -0.000418894     -0.00225118    0.000297788

In [104]:
sort(abs.(xₜ[:]),rev=true)


Out[104]:
35344-element Array{Float32,1}:
 3.0461323
 3.030546
 3.0275538
 3.0268679
 3.0247257
 3.0191596
 3.0138164
 2.9978106
 2.9913576
 2.9854884
 2.983375
 2.9723668
 2.9717646
 ⋮
 4.5079918f-7
 3.8863436f-7
 3.5576522f-7
 3.4145432f-7
 3.2693424f-7
 2.9928253f-7
 2.4439942f-7
 1.7671846f-7
 1.4750549f-7
 1.2071314f-7
 9.795443f-8
 6.2558684f-8

In [105]:
ind=sortperm(abs.(vec(xₜ)),rev=true)
# 0.1 = 10%, try also 0.05 = 5%
τ=0.05
k=round(Int,τ*length(ind))
xsparse=copy(xₜ)
# xsparse.*=abs.(xsparse).>=0.3
xsparse[ind[k+1:end]].=0
# Inverse wavelet transform of the sparse data
imgsparse=idwt(xsparse,wlet,2)
# Original and sparse image
display(img)
display(colorview(Gray,imgsparse))
" Number of non-zero elements = ",nnz(sparse(xsparse))


Out[105]:
(" Number of non-zero elements = ", 1767)

In [106]:
# Columnwise number of non-zeros 
nz=[nnz(sparse(xsparse[:,i])) for i=1:size(x,2)]
display(nz)
maximum(nz)


188-element Array{Int64,1}:
  8
 13
 16
 30
 41
 45
 45
 43
 45
 44
 43
 46
 46
  ⋮
  0
  0
  0
  0
  0
  0
  0
  0
  0
  0
  0
  0
Out[106]:
46

In [122]:
# Sampling
n=size(xsparse,2)
m=100
A=rand(Normal(0,1/m),m,n)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Sampling (columnwise)
b=A*xsparse


Out[122]:
100×188 Array{Float64,2}:
 -0.0478574   -0.438766   -0.293638   …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.144427     0.155343    0.0207098     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.0754137   -0.113392   -0.256703      0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.0230087   -0.0134382  -0.0749675     0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.0098781   -0.171348   -0.0849669     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.0423878    0.184341    0.160656   …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.00657431   0.220219    0.0287134     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.204276    -0.027439   -0.101387      0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.0174248   -0.183895    0.080822      0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.2681      -0.12869    -0.0226446     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.0871245   -0.0875363  -0.119802   …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.170071    -0.105195    0.110028      0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.0289974    0.0762489   0.048376      0.0  0.0  0.0  0.0  0.0  0.0  0.0
  ⋮                                   ⋱                      ⋮         
 -0.238952    -0.113758   -0.116537      0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.334004    -0.249185   -0.419488      0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.227125    -0.422397   -0.467045   …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.089014    -0.156352   -0.299777      0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.105088    -0.174521   -0.149469      0.0  0.0  0.0  0.0  0.0  0.0  0.0
 -0.0113261    0.183824   -0.0691255     0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.0746897    0.104549    0.125095      0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.194566     0.201428    0.295203   …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.0495381   -0.233511   -0.178785      0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.124598     0.212267    0.259033      0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.0799726    0.12365     0.0164697     0.0  0.0  0.0  0.0  0.0  0.0  0.0
  0.11155      0.18528     0.122349      0.0  0.0  0.0  0.0  0.0  0.0  0.0

In [123]:
# Recovery
xrecover=similar(xsparse)
for l=1:size(xsparse,2)
    l1=linprog(ones(n),A,'=',b[:,l],0,Inf,
        ClpSolver())
    xrecover[:,l]=l1.sol
end

In [124]:
A


Out[124]:
100×188 Array{Float64,2}:
 -0.0945303    0.0674013   -0.0938062   …   0.00400639   0.170677
 -0.0534955   -0.0626742   -0.016279        0.111543    -0.130198
  0.0362648   -0.00553627   0.0336504      -0.107911     0.095615
  0.105129     0.0377616   -0.0635065       0.0156088   -0.0549372
  0.0306301    0.131948     0.00150744      0.0455267    0.159873
  0.113495     0.0574179    0.129195    …  -0.0101835    0.188066
  0.140386    -0.106888     0.0941055       0.00150008   0.0769297
 -0.0913932   -0.0580505   -0.164214        0.00895488  -0.00396488
 -0.311914    -0.0560556    0.0378786       0.0592258   -0.2196
 -0.055356     0.10234     -0.0164797       0.0888232    0.0483672
  0.0622768   -0.102535     0.0272376   …  -0.158351    -0.0104979
 -0.0715572   -0.064994    -0.057714       -0.035952    -0.000788881
  0.0241719    0.0264769    0.0212786      -0.135075    -0.227646
  ⋮                                     ⋱               
 -0.0437062    0.0478605   -0.0545285       0.128717     0.170254
 -0.025153    -0.0650193   -0.169966       -0.0216274   -0.0956563
 -0.0518907   -0.0574118    0.115515    …  -0.0302926   -0.103205
 -0.00607879   0.00382582   0.0221078      -0.0921099    0.0203513
  0.0739161   -0.0536984   -0.0917377      -0.0562021   -0.0377515
  0.130137     0.101431     0.131589       -0.0697302    0.0313715
  0.122271    -0.164899     0.0909412       0.173354    -0.00514462
  0.0832469    0.0690692   -0.0661704   …   0.0368434    0.0913525
  0.0574606    0.125494    -0.0302353      -0.184996    -0.0692399
 -0.0148349   -0.116441     0.189246        0.114252    -0.0124487
  0.0936629    0.037915    -0.132571        0.236181     0.00606293
  0.215453     0.185037    -0.105911       -0.0585124    0.0126269

In [125]:
xsparse


Out[125]:
188×188 Array{Float32,2}:
 0.0       0.464983  0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.4936    …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.466739  0.46553   0.485944  0.485925     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.47291   0.466418  0.476396  0.473248     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.469399  0.481726  0.493102  0.505303     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.46619   0.484553  0.514593     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 ⋮                                       ⋱                      ⋮         
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0

In [126]:
xrecover


Out[126]:
188×188 Array{Float32,2}:
 0.0       0.464983  0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.4936    …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.466739  0.46553   0.485944  0.485925     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.47291   0.466418  0.476396  0.473248     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.469399  0.481726  0.493102  0.505303     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.46619   0.484553  0.514593     0.0  0.0  0.0  0.0  0.0  0.0  0.0
 ⋮                                       ⋱                      ⋮         
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0       …  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0       0.0       0.0       0.0          0.0  0.0  0.0  0.0  0.0  0.0  0.0

In [127]:
imgrecover=idwt(xrecover,wlet)
# Original and recovered image
display(img)
display(colorview(Gray,imgrecover))